Red Hat Enterprise Linux Implementation

Bash Conditionals and Control Structures

Module Topics

  • Using Bash Special Variables

  • Evaluating Exit Codes

  • Evaluating Exit Codes

  • Testing Script Inputs

  • Using Conditional Structures

Using Bash Special Variables

Positional Parameters
  • Predefined variables that store value of command-line arguments

  • Named numerically:

    • 0 refers to script name

    • 1 is predefined with value of first script argument

    • 2 contains second argument, and so on

  • Values referenced with syntax $1, $2, etc.

When referencing values past the ninth positional parameter, the brace-quoted form of variable expansion must be used. For example, the value of the tenth positional argument must be referenced with the syntax ${10} rather than $10. Otherwise, Bash expands the $1 in $10 to the value of the first positional argument to the script.

Using Bash Special Variables

  • Special variables refer to positional parameters: $* and $@

    • When $* is used, all arguments are seen as single word

    • When $@ is used, each argument is seen as separate word

      [student@server1 bin]$ cat showargs
      #!/bin/bash
      
      for ARG in "$*"; do
          echo $ARG
      done
      
      [student@server1 bin]$ ./showargs 1 2 3
      1 2 3
      
      [student@server1 bin]$ cat showargs
      #!/bin/bash
      
      for ARG in "$@"; do
          echo $ARG
      done
      
      [student@server1 bin]$ ./showargs 1 2 3
      1
      2
      3

Using Bash Special Variables

  • $# represents number of command-line arguments passed to a script

  • Can be used to verify whether any arguments, or correct number of arguments, are passed to script

    [student@server1 bin]$ cat countargs
    #!/bin/bash
    echo "There are $# arguments."
    
    [student@server1 bin]$ ./countargs
    There are 0 arguments.
    
    [student@server1 bin]$ ./countargs 1 2 3
    There are 3 arguments.

Evaluating Exit Codes

Exit Status
  • Every command returns exit status

    • Also called return status or exit code

  • Successful command exits with status 0

  • Unsuccessful commands exit with nonzero status

  • Exit status is passed to parent process and stored in ? variable

  • Display value of $? to retrieve exit status of executed command

Evaluating Exit Codes

[student@server1 bin]$
ls /etc/hosts

/etc/hosts

[student@server1 bin]$ echo $?
0

[student@server1 bin]$ ls /etc/nofile
ls: cannot access /etc/nofile: No such file or directory

[student@server1 bin]$ echo $?
2

[student@server1 bin]$ grep localhost /etc/hosts
127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
::1         localhost localhost.localdomain localhost6 localhost6.localdomain6

[student@server1 bin]$ echo $?
0

[student@server1 bin]$ grep random /etc/hosts

[student@server1 bin]$ echo $?
1

Evaluating Exit Codes

Using Exit Codes
  • Script exits when it has processed its contents

  • May want script to exit part way through, such as when error occurs

  • When script encounters exit, it immediately stops processing and exits

  • Can execute`exit` with optional integer argument between 0 and 255 (exit code)

    • 0 represents no error

    • Nonzero values indicate error

      • Use different nonzero values to differentiate error types

Evaluating Exit Codes

  • Exit code is passed back to parent process and stored in ? variable

  • To retrieve value, use $?

  • If exit is used without argument, script exits and passes exit status of last command executed

    [student@server1 bin]$ cat hello
    #!/bin/bash
    echo "Hello, world"
    exit 0
    
    [student@server1 bin]$ ./hello
    Hello, world
    
    [student@server1 bin]$ echo $?
    0
    [student@server1 bin]$ cat hello
    #!/bin/bash
    echo "Hello, world"
    exit 1
    
    [student@server1 bin]$ ./hello
    Hello, world
    
    [student@server1 bin]$ echo $?
    1

Testing Script Inputs

  • To perform integrity checking, use test

    • Can also use newer extended test command syntax [[ <TESTEXPRESSION> ]]

  • Upon completion, test produces exit code stored as $?

  • Exit status 0 indicates test passed, and nonzero values indicate failure

Testing Script Inputs

Performing Comparison Tests
  • Comparison test expressions use binary operators

  • Operators expect object on each side of operator and evaluate objects for equality and inequality

    [ <ITEM1> <BINARY COMPARISON OPERATOR> <ITEM2> ]
  • Bash uses different operators for string and numeric comparisons

  • Binary numeric comparison is limited to integers

Testing Script Inputs

Operator

Meaning

Example

-eq

is equal to

[ "$a" -eq "$b" ]

-ne

is not equal to

[ "$a" -ne "$b" ]

-gt

is greater than

[ "$a" -gt "$b" ]

-ge

is greater than or equal to

[ "$a" -ge "$b" ]

-lt

is less than

[ "$a" -lt "$b" ]

-le

is less than or equal to

[ "$a" -le "$b" ]

Testing Script Inputs

Example Numeric Comparison Operators
[student@server1 ~]$ [ 1 -eq 1 ]; echo $?
0
[student@server1 ~]$ [ 1 -ne 1 ]; echo $?
1
[student@server1 ~]$ [ 8 -gt 2 ]; echo $?
0
[student@server1 ~]$ [ 2 -ge 2 ]; echo $?
0
[student@server1 ~]$ [ 2 -lt 2 ]; echo $?
1
[student@server1 ~]$ [ 1 -lt 2 ]; echo $?
0

Testing Script Inputs

  • String comparison uses following binary operators

Operator

Meaning

Example

=

is equal to

[ "$a" = "$b" ]

==

is equal to

[ "$a" == "$b" ]

!=

is not equal to

[ "$a" != "$b" ]

  • Examples of Bash’s string comparison operators

    [student@server1 ~]$ [ abc = abc ]; echo $?
    0
    [student@server1 ~]$ [ abc == def ]; echo $?
    1
    [student@server1 ~]$ [ abc != def ]; echo $?
    0

Testing Script Inputs

Unary Operators
  • Bash also has unary operators for string evaluation

  • Unary operators evaluate just one item

    [ <UNARY OPERATOR> <ITEM> ]
  • Unary operators for string evaluation

Operator

Meaning

Example

-z

string is zero length (null)

[ -z "$a" ]

-n

string is not null

[ -n "$a" ]

  • Examples of string unary operators

    [student@server1 ~]$ STRING=''; [ -z "$STRING" ]; echo $?
    0
    [student@server1 ~]$ STRING='abc'; [ -n "$STRING" ]; echo $?
    0

Testing Script Inputs

Testing Files and Directories
  • Test script interactions with external entities, such as files and directories

Operator

Meaning

Example

-b

File exists and is block special

[ -b <FILE> ]

-c

File exists and is character special

[ -c <FILE> ]

-d

File exists and is a directory

[ -d <DIRECTORY> ]

-e

File exists

[ -e <FILE> ]

-f

File is a regular file

[ -f <FILE> ]

-L

File exists and is a symbolic link

[ -L <FILE> ]

-r

File exists and read permission is granted

[ -r <FILE> ]

-s

File exists and has a size greater than zero

[ -s <FILE> ]

-w

File exists and write permission is granted

[ -w <FILE> ]

-x

File exists and execute (or search) permission is granted

[ -x <FILE> ]

Testing Script Inputs

Operator

Meaning

Example

-ef

FILE1 has the same device and inode number as FILE2

[ <FILE1> -ef <FILE2> ]

-nt

FILE1 has newer modification date than FILE2

[ <FILE1> -nt <FILE2> ]

-ot

FILE1 has older modification date than FILE2

[ <FILE1> -ot <FILE2> ]

  • Space character is required in these locations:

    • Inside test expression brackets

    • To separate elements within test expression

Testing Script Inputs

Logical AND OR Operators
  • Use logical AND operator && to test if both of two conditions are true

  • Use logical OR operator || to test if either of two conditions is true

    [student@server1 ~]$ [ 2 -gt 1 ] && [ 1 -gt 0 ]; echo $?
    0
    [student@server1 ~]$ [ 2 -gt 1 ] && [ 1 -gt 2 ]; echo $?
    1
    [student@server1 ~]$ [ 2 -gt 1 ] || [ 1 -gt 2 ]; echo $?
    0
    [student@server1 ~]$ [ 0 -gt 1 ] || [ 1 -gt 2 ]; echo $?
    1

Using Conditional Structures

  • Simple shell scripts are a series of commands that execute from beginning to end

  • Adding conditional structures allows scripts to incorporate decision-making

    • Portions of script execute only when certain conditions are met

Using Conditional Structures

if/then Statement
  • Simplest conditional structure

    if <CONDITION>; then
          <STATEMENT>
          ...
          <STATEMENT>
        fi
  • If condition is met, action is taken

  • Numeric, string, and file tests are frequently used as conditions in if/then statements

    • Example: Use if/then statement to start psacct service if it is not active

      systemctl is-active psacct > /dev/null 2>&1
      
      if  [ $? -ne 0 ]; then
        systemctl start psacct
      fi

Using Conditional Structures

if/then/else Statement
  • Different actions are taken based on whether condition is met

    if <CONDITION>; then
          <STATEMENT>
          ...
          <STATEMENT>
        else
          <STATEMENT>
          ...
          <STATEMENT>
    fi
    • Example: Use if/then/else to start psacct service if it is not active and stop it if it is active

      systemctl is-active psacct > /dev/null 2>&1
      
      if  [ $? -ne 0 ]; then
        systemctl start psacct
      else
        systemctl stop psacct
      fi

Using Conditional Structures

if/then/elif/then/else Statement
  • To test multiple conditions, use elif (ELSE IF)

  • Bash tests conditions in order

  • Upon finding true condition:

    • Bash executes actions associated with condition

    • Bash skips remainder of conditional structure

  • If no conditions is true, Bash executes else actions

    if <CONDITION>; then
          <STATEMENT>
          ...
          <STATEMENT>
        elif <CONDITION>; then
          <STATEMENT>
          ...
          <STATEMENT>
        else
          <STATEMENT>
          ...
          <STATEMENT>
        fi

Using Conditional Structures

  • Example: Use if/then/elif/then/else statement to:

    • Run mysql client if mariadb is active

    • Run psql client if postgresql is active

    • Or, run sqlite3 client if both mariadb and postgresql are not active

      systemctl is-active mariadb > /dev/null 2>&1
      MARIADB_ACTIVE=$?
      systemctl is-active postgresql > /dev/null 2>&1
      POSTGRESQL_ACTIVE=$?
      
      if  [ "$MARIADB_ACTIVE" -eq 0 ]; then
        mysql
      elif  [ "$POSTGRESQL_ACTIVE" -eq 0 ]; then
        psql
      else
        sqlite3
      fi

Using Conditional Structures

Case Statement
  • if/then/elif/then/else statement can have as many elif clauses as needed

  • Statement and logic become harder to read and understand

  • For complex situations, use case statements

    case <VALUE> in
       <PATTERN1>)
           <STATEMENT>
           ...
           <STATEMENT>
           ;;
       <PATTERN2>)
           <STATEMENT>
           ...
           <STATEMENT>
           ;;
    esac

Using Conditional Structures

  • case statement attempts to match <VALUE> to each <PATTERN> in order, one by one

    • When pattern matches, code segment associated with pattern is executed

      • ;; indicates end of block

    • All other patterns are skipped and case statement is exited

    • Can add as many pattern/statement blocks as needed

  • To mimic behavior of else in if/then/elif/then/else, use * as final pattern

  • Because * matches anything, it executes a set of commands if no other patterns were matched

  • case statements are widely used in init scripts

    case "$1" in
       start)
           start
           ;;
       stop)
           rm -f $lockfile
           stop
           ;;
       restart)
           restart
           ;;
       reload)
           reload
           ;;
       status)
           status
           ;;
       *)
           echo "Usage: $0 (start|stop|restart|reload|status)"
           ;;
    esac

Using Conditional Structures

  • case statements are widely used in init scripts

    case "$1" in
       start)
           start
           ;;
       stop)
           rm -f $lockfile
           stop
           ;;
       restart)
           restart
           ;;
       reload)
           reload
           ;;
       status)
           status
           ;;
       *)
           echo "Usage: $0 (start|stop|restart|reload|status)"
           ;;
    esac

Using Conditional Structures

  • If action is same for multiple patterns, can combine patterns to share action block

  • To separate multiple patterns, use pipe character, |

    case "$1" in
       ...
       reload|restart)
           restart
           ;;
       ...
    esac
References
  • Man pages: bash(1) and test(1)

Summary

  • Using Bash Special Variables

  • Evaluating Exit Codes

  • Evaluating Exit Codes

  • Testing Script Inputs

  • Using Conditional Structures

Module Completion

Nice job!

Click the button below to complete this module of the course: